/* $RCSFile: CrmBulkDelete.ascx.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/AddonNice/PageModules/CrmBulkDelete/CrmBulkDelete.ascx.cs $
 * $log$
 * Revision 20 2010/08/09 10:51:19  christian.surieux
 *   Added Log comment to all .aspx .ascx .master files
 * Revision 19 2010/08/09 09:43:47  christian.surieux
 *   Added Log to cs sources files
 * Revision 6 2010/08/06 19:56:24  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Data;
using System.Drawing;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;
using System.Data.SqlClient;
using System.Web.Services.Protocols;
using System.Globalization;

using AddonNice.UI;
using AddonNice.Design;
using AddonNice.UI.WebControls;
using AddonNice.Security;
using AddonNice.UI.DataTypes;
using AddonNice.Configuration;
using AddonNice.Configuration.Settings;
using AddonNice.Diagnostics;
using AddonNice.Helpers;
using AddonNice.Settings;

using AddonNice.CRMWrapper;

using Telerik.Web.UI;

using Trace2    =   System.Diagnostics.Trace;

namespace AddonNice.PageModules.CrmBulkDelete
{
 	#region Local TraceSwitch 
	
    public class CrmBulkDeleteTrace : ADNTraceSwitch
	{
		static public ADNTraceSwitch	Sw	            =	new ADNTraceSwitch("CrmBulkDeleteTrace","Special Trace Switch for BulkDelete");
	}
	
    #endregion Local TraceSwitch 

    public class AStr
    {
        public const string Subject                 =   "BULK_DELETE_SUBJECT";
    }
    
    /// <summary>
    /// Initializer class for CrmBulkDelete 
    /// </summary>
    public class CrmBulkDeleteSI : PortalCRMModuleControlSI
    {
        public CrmBulkDeleteSI(ModuleSettingsDictionary bSettings,ISettingsOwner pm) : base()
        {
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDeleteSI Ctor BEG.",CrmBulkDeleteTrace.Sw.Info);
            InitSettings(bSettings,pm);
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDeleteSI Ctor END.",CrmBulkDeleteTrace.Sw.Info);
        }
 
        public override void InitSettings(ModuleSettingsDictionary bSettings,ISettingsOwner pm)
        {             
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDeleteSI InitSettings BEG.",CrmBulkDeleteTrace.Sw.Info);
            
            base.InitSettings(bSettings,pm);

			// Specific parameters
//            const string extgrp                 =   "CRMBULKDELETE";
/*            
            SettingItemSpecial AttributeOwner   =   new SettingItemSpecial(AStr.AssignToOwner,new BooleanDataType());
            AttributeOwner.Required             =   false;
            AttributeOwner.Value                =   StStr.strFalse;
            AttributeOwner.Order                =   10;
            AttributeOwner.ExtendedGroupName    =   extgrp;
            AttributeOwner.EnglishName          =   "Assign to Owner";
            AttributeOwner.Description          =   "When checked, the customers will be automatically assigned to selected owner";
            bSettings.AddSetting(AttributeOwner);
            */
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDeleteSI InitSettings END.",CrmBulkDeleteTrace.Sw.Info);
        }
    }

    /// <summary>
    /// e={0}&id={1}&c={2}&m={3}",new object[]{MemberTypename,dynCust.PrimaryKeyGuidValue,CampaignId,mail.activityid});
    ///	Dedicated input (only) module for leads
    /// </summary>
    public partial class CrmBulkDelete : PortalCRMModuleControl
    {
        /// <summary>
        /// No entity needed here
        /// </summary>
        public override bool NeedAttachedEntity
        {
            get
            {
                return false;
            }
        }

        #region General implementation
        
        public override Guid GuidID
        {
            get
            {
                return new Guid("{13F8A8F3-AEDB-47df-82DF-A3906A10547F}");
            }
        }
        #endregion General implementation

        Guid JobId
        {
            get
            {
                object ob   =   ViewState["JobId"];
                if ( ob == null )
                    return Guid.Empty;
                return (Guid)ob;
            }
            set
            {
                ViewState["JobId"]  =   value;
            }
        }

        Guid UserQueryID            
        {
            get
            {
                object ob   =   ViewState["UserQueryID"];
                if ( ob == null )
                    return Guid.Empty;
                return (Guid)ob;
            }
            set
            {
                ViewState["UserQueryID"]  =   value;
            }
        }

        Guid SavedQueryID            
        {
            get
            {
                object ob   =   ViewState["SavedQueryID"];
                if ( ob == null )
                    return Guid.Empty;
                return (Guid)ob;
            }
            set
            {
                ViewState["SavedQueryID"]  =   value;
            }
        }

        public CrmBulkDelete()
        {
            if ( ModuleTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDelete Ctor Called.",ModuleTraceSwitch.Sw.Info);
            // Validators : name Ok
            ValidationGroup                     =   "Main";

        }

        public override void InitPortalModuleControl(ModuleSettingsDictionary bSettings)
        {
            PortalModuleInitializer =   new CrmBulkDeleteSI(bSettings,this);
        }
    
        /// <summary>
		///  The Page_Load event handler on this User Control is used to
		/// to load the list of the questions and civilities  
		/// </summary>
        protected void Page_Load(object sender, EventArgs e)
        {
            if (ModuleTraceSwitch.Sw.TraceVerbose | CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete Page_Load isPostBack: {0}",IsPostBack),(ModuleTraceSwitch.Sw.TraceVerbose) ? ModuleTraceSwitch.Sw.Info:CrmBulkDeleteTrace.Sw.Info);
            if (!IsPostBack)
            {
                // fill the entities combo
                CbEntity.DataTextField  =   "displayName";
                CbEntity.DataValueField =   "entityName";
                CbEntity.DataSource     =   GetAllEntities(false);
                CbEntity.DataBind();
                // Select lead
                RadComboBoxItem it      =   CbEntity.Items.FindItemByValue(CrmStr.lead);
                it.Selected             =   true;

                it                      =   new RadComboBoxItem(LocalizeHelper.Localize("BULK_DELETE_USERQUERY","User Query"),CrmStr.userquery);
                it.Selected             =   true;
                CbQueryType.Items.Add(it);
                it                      =   new RadComboBoxItem(LocalizeHelper.Localize("BULK_DELETE_SAVEDQUERY","Saved Query"),CrmStr.savedquery);
                CbQueryType.Items.Add(it);

                // Fill the query combo, default to lead
                FillQueries(CrmStr.lead);

                // Fill the recurrence
                it                      =   new RadComboBoxItem("None","none");
                it.Selected             =   true;
                CbRecurrence.Items.Add(it);
                it                      =   new RadComboBoxItem("minutely",CRMWrapper.RecurrencePattern.minutely);
                CbRecurrence.Items.Add(it);
                it                      =   new RadComboBoxItem("hourly",CRMWrapper.RecurrencePattern.hourly);
                CbRecurrence.Items.Add(it);
                it                      =   new RadComboBoxItem("dayly",CRMWrapper.RecurrencePattern.dayly);
                CbRecurrence.Items.Add(it);
                it                      =   new RadComboBoxItem("weekly",CRMWrapper.RecurrencePattern.weekly);
                CbRecurrence.Items.Add(it);
                it                      =   new RadComboBoxItem("monthly",CRMWrapper.RecurrencePattern.monthly);
                CbRecurrence.Items.Add(it);

            }
        }
        
        ArrayList _AllEntities =   null;
        ArrayList GetAllEntities(bool useAll)
        {
            if (_AllEntities == null)
            {
                _AllEntities          =   MetadataCache.GetAllEntities(Wrapper,useAll);
            }
            return _AllEntities;
        }

        void CrmBulkDelete_ADNUpdateModule(object sender, ModuleEventArgs e)
        {
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine("[V]CrmBulkDelete CrmBulkDelete_ADNUpdateModule BEG.",CrmBulkDeleteTrace.Sw.Info);
            try
            {
                string jobTitle             =   TbJobTitle.Text.Trim();
                Guid queryId                =   new Guid(CbQueries.SelectedValue);
                ADNDynSavedquery uQ         =   DynEntity.GetFromCache<ADNDynSavedquery>(CrmStr.savedquery,Wrapper,ResetWrapper,queryId);
                bool sendMail               =   CkSendMail.Checked;
                Guid[]  ccRecipients        =   new Guid[0];
                Guid[] ToRecipients         =   new Guid[0];
                if ( sendMail )
                {
                    if ( !string.IsNullOrEmpty(CbMailTo.SelectedValue) )
                    {
                        ToRecipients       =   new Guid[]{new Guid(CbMailTo.SelectedValue)};
                    }
                    else sendMail           =   false;
                }
                string recurPattern         =   CbRecurrence.SelectedValue;
                if ( recurPattern == "none" )
                    recurPattern            =   string.Empty;
                string fetchxml             =   uQ.fetchxml;
                if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete CrmBulkDelete_ADNUpdateModule fetchxml: {0}",fetchxml),CrmBulkDeleteTrace.Sw.Info);
                Guid retJobId               =   Wrapper.Util.BulkDelete(jobTitle,fetchxml,sendMail,ToRecipients,ccRecipients,recurPattern,(TpStartDate.SelectedDate.HasValue) ? TpStartDate.SelectedDate.Value:DateTime.Now,false);
                JobId                       =   retJobId;
                LblMsg.Text                 =   string.Format("CreateDataSource JobId: {0}",retJobId);
                if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete CrmBulkDelete_ADNUpdateModule END jobId: {0}",retJobId),CrmBulkDeleteTrace.Sw.Info);
            }
            catch ( Exception ex )
            {
                if ( CrmBulkDeleteTrace.Sw.TraceError)
                    Trace2.WriteLine(string.Format(CultureInfo.InvariantCulture,"[E]CrmBulkDelete CrmBulkDelete_ADNUpdateModule ex: {0}",ex),CrmBulkDeleteTrace.Sw.Info);
                LblMsg.Text                 =   "Error in query";
            }
             if ( CrmBulkDeleteTrace.Sw.TraceVerbose)
                Trace2.WriteLine("[V]CrmBulkDelete CrmBulkDelete_ADNUpdateModule END.",CrmBulkDeleteTrace.Sw.Info);
        }

        /// <summary>
        /// Reset Query Combo
        /// </summary>
        void CbEntity_SelectedIndexChanged(object o,RadComboBoxSelectedIndexChangedEventArgs e)
        {
            string entName                  =   e.Value;
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete CbEntity_SelectedIndexChanged CALLED entName: {0}",entName),CrmBulkDeleteTrace.Sw.Info);
            FillQueries(entName);
            RadGrid1.DataSource             =   null;
            RadGrid1.Rebind();
        }
        
        void FillQueries(string entName)
        {
            bool isUserQuery            =   CbQueryType.SelectedItem.Value == CrmStr.userquery;
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete FillQueries BEG entName: {0}, isUserQuery: {1}",entName,isUserQuery),CrmBulkDeleteTrace.Sw.Info);
            DataTable Tb                =   (isUserQuery) ? Wrapper.Util.BuildUserQueryTable(entName,ResetWrapper):Wrapper.Util.BuildSavedQueryTable(entName,ResetWrapper);
            CbQueries.Items.Clear();
            CbQueries.DataTextField     =   CrmStr.name;
            CbQueries.DataValueField    =   (isUserQuery) ? CrmStr.userqueryid: CrmStr.savedqueryid;
            CbQueries.DataSource        =   Tb;
            CbQueries.DataBind();
            if ( CbQueries.Items.Count > 0 )
            {
                CbQueries.Items[0].Selected   =   true;
                if ( isUserQuery )
                {
                    if ( ! string.IsNullOrEmpty(CbQueries.SelectedValue) )
                            UserQueryID     =   new Guid(CbQueries.SelectedValue);
                    else    UserQueryID     =   Guid.Empty;
                    SavedQueryID            =   Guid.Empty;
                }
                else
                {
                    if ( ! string.IsNullOrEmpty(CbQueries.SelectedValue) )
                            SavedQueryID    =  new Guid(CbQueries.SelectedValue);
                    else    SavedQueryID    =  Guid.Empty;
                    UserQueryID             =  Guid.Empty;
                }
            }
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete FillQueries END count: {0}",Tb.Rows.Count),CrmBulkDeleteTrace.Sw.Info);
        }

        void CbQueries_SelectedIndexChanged(object o,RadComboBoxSelectedIndexChangedEventArgs e)
        {
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete CbQueries_SelectedIndexChanged BEG e.value: {0}",e.Value),CrmBulkDeleteTrace.Sw.Info);
            bool isUserQuery            =   CbQueryType.SelectedItem.Value == CrmStr.userquery;
            if ( isUserQuery )
                 UserQueryID            =   new Guid(e.Value);
            else SavedQueryID           =   new Guid(e.Value);
            RadGrid1.DataSource         =   null;
            RadGrid1.Rebind();
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine("[V]CrmBulkDelete CbQueries_SelectedIndexChanged END.",CrmBulkDeleteTrace.Sw.Info);
        }

        void CbQueryType_SelectedIndexChanged(object o,RadComboBoxSelectedIndexChangedEventArgs e)
        {
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete CbQueryType_SelectedIndexChanged BEG e.value: {0}",e.Value),CrmBulkDeleteTrace.Sw.Info);
            FillQueries(CbEntity.SelectedValue);
            RadGrid1.DataSource             =   null;
            RadGrid1.Rebind();
            if ( CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine("[V]CrmBulkDelete CbQueryType_SelectedIndexChanged END.",CrmBulkDeleteTrace.Sw.Info);
        }


        void RadGrid1_NeedDataSource(object source,GridNeedDataSourceEventArgs e)
        {
            bool isUserQuery            =   CbQueryType.SelectedItem.Value == CrmStr.userquery;
            if ( ( isUserQuery && UserQueryID == Guid.Empty ) || ( !isUserQuery && SavedQueryID == Guid.Empty ) )
            {
                RadGrid1.DataSource     =   null;
                return;
            }
            try
            {
                if ( isUserQuery )
                {
                    ADNDynUserquery query   =   DynEntity.GetFromCache<ADNDynUserquery>(CrmStr.userquery,Wrapper,ResetWrapper,UserQueryID);
                    RadGrid1.DataSource     =   query.ExecuteAsTb(true,ResetWrapper);
                }
                else
                {
                    ADNDynSavedquery query  =   DynEntity.GetFromCache<ADNDynSavedquery>(CrmStr.savedquery,Wrapper,ResetWrapper,SavedQueryID);
                    RadGrid1.DataSource     =   query.ExecuteAsTb(true,ResetWrapper);
                }
            }
            catch ( Exception ex )
            {
                if ( CrmBulkDeleteTrace.Sw.TraceError)
                    Trace2.WriteLine(string.Format(CultureInfo.InvariantCulture,"[E]CrmBulkDelete RadGrid1_NeedDataSource ex: {0}",ex),CrmBulkDeleteTrace.Sw.Info);
                LblMsg.Text                 =   LocalizeHelper.Localize("BULK_DELETE_INVALIDVIEW","Invalid view");
            }
        }

        void RadGrid1_ColumnCreated(object sender,GridColumnCreatedEventArgs e)
        {
            // try to avoid id columns
            if ( e.Column.HeaderText == CbEntity.SelectedValue+"id" )
                e.Column.Display    =   false;
            if ( e.Column.DataType == typeof(Guid))
                e.Column.Display    =   false;
        }

        void CbStates_SelectedIndexChanged(object o,RadComboBoxSelectedIndexChangedEventArgs e)
        {
            RadGridJobs.DataSource  =   null;
            RadGridJobs.Rebind();
        }

        /// <summary>
        /// Refresh the job list
        /// </summary>
        void RefreshBtn_Click(object sender,ImageClickEventArgs e)
        {
            RadGridJobs.DataSource  =   null;
            RadGridJobs.Rebind();
        }
        /*
Ready 0 Specifies that the bulk delete operation is ready. Waiting for Resources 0 

Suspended 1 Specifies that the bulk delete operation is suspended. Waiting 10, Retrying 11, Paused 12
 
Locked 2 Specifies that the bulk delete operation is locked. In Progress 20,Pausing 21, Canceling 22

Completed 3 Specifies that the bulk delete operation is completed. Succeeded 30,Failed 31,Canceled 32
         * */
        /// <summary>
        /// Fiil grid with list of running jobs
        /// </summary>
        void RadGridJobs_NeedDataSource(object source,GridNeedDataSourceEventArgs e)
        {
             if ( CrmBulkDeleteTrace.Sw.TraceVerbose)
                Trace2.WriteLine("[V]CrmBulkDelete RadGridJobs_NeedDataSource BEG.",CrmBulkDeleteTrace.Sw.Info);
            try
            {
                InternalQueryExpression qe                      =   new InternalQueryExpression();
                InternalColumnSet Cols                          =   new InternalColumnSet();
                Cols.Attributes                                 =   new string[]{CrmStr.asyncoperationid,CrmStr.name,CrmStr.statecode,CrmStr.statuscode,CrmStr.operationtype,CrmStr.postponeuntil,CrmStr.createdon,CrmStr.startedon,CrmStr.completedon};
                qe.ColumnSet                                    =   Cols;
                qe.EntityName                                   =   CrmStr.asyncoperation;
                int statJob                                     =   int.Parse(CbStates.SelectedValue);
                InternalFilterExpression filter                 =   new InternalFilterExpression();
            
                filter.FilterOperator                           =   InternalLogicalOperator.And;
                InternalConditionExpression cond                =   new InternalConditionExpression();
                cond.AttributeName                              =   CrmStr.operationtype;
                cond.Operator                                   =   InternalConditionOperator.Equal;
                cond.Values                                     =   new object[]{13};

                if ( CrmBulkDeleteTrace.Sw.TraceVerbose)
                    Trace2.WriteLine(string.Format(CultureInfo.InvariantCulture,"[V]CrmBulkDelete RadGridJobs_NeedDataSource statJob: {0}",statJob),CrmBulkDeleteTrace.Sw.Info);
                if ( statJob != -1 )
                {
                    InternalConditionExpression condActif       =   new InternalConditionExpression();
                    condActif.AttributeName                     =   CrmStr.statecode;
                    condActif.Operator                          =   InternalConditionOperator.Equal;
                    condActif.Values                            =   new object[]{statJob};

                    filter.Conditions                           =   new InternalConditionExpression[] {cond,condActif};
                }
                else filter.Conditions                           =   new InternalConditionExpression[] {cond};

                qe.Criteria                                 =   filter;


                //GenericDynQueryMultiple<ADNDynBulkdeleteoperation> allEntQuery      =   new GenericDynQueryMultiple<ADNDynBulkdeleteoperation>(Wrapper,ResetWrapper,CrmStr.bulkdeleteoperation);
                GenericDynQueryMultiple<ADNDynAsyncoperation> allEntQuery      =   new GenericDynQueryMultiple<ADNDynAsyncoperation>(Wrapper,ResetWrapper,CrmStr.asyncoperation);
                DataTable Tb                                    =   allEntQuery.retrieveDatatable(qe);
                RadGridJobs.DataSource                          =   Tb;
            }
            catch ( Exception ex )
            {
                if ( CrmBulkDeleteTrace.Sw.TraceError)
                    Trace2.WriteLine(string.Format(CultureInfo.InvariantCulture,"[E]CrmBulkDelete RadGridJobs_NeedDataSource ex: {0}",ex),CrmBulkDeleteTrace.Sw.Info);
            }
             if ( CrmBulkDeleteTrace.Sw.TraceVerbose)
                Trace2.WriteLine("[V]CrmBulkDelete RadGridJobs_NeedDataSource END.",CrmBulkDeleteTrace.Sw.Info);
        }

        #region Std overrides

        public override void Page_PreInit(object sender, EventArgs e)
        {
            base.Page_PreInit(sender, e);
            updateBtn.SkinID = "Update_" + Helpers.LocalizeHelper.GetCurrentNeutralCultureName();
            RefreshBtn.SkinID = "Update_" + Helpers.LocalizeHelper.GetCurrentNeutralCultureName();
        }

        override protected void OnInit(EventArgs e)
        {
            if (ModuleTraceSwitch.Sw.TraceVerbose | CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDelete OnInit BEG.",(ModuleTraceSwitch.Sw.TraceVerbose) ? ModuleTraceSwitch.Sw.Info:CrmBulkDeleteTrace.Sw.Info);
            InitializeComponent();
            base.OnInit(e);
            if (ModuleTraceSwitch.Sw.TraceVerbose | CrmBulkDeleteTrace.Sw.TraceVerbose )
                Trace2.WriteLine( "[V]CrmBulkDelete OnInit END.",(ModuleTraceSwitch.Sw.TraceVerbose) ? ModuleTraceSwitch.Sw.Info:CrmBulkDeleteTrace.Sw.Info);
        }

        private void InitializeComponent()
        {
            Load                            +=  new System.EventHandler(Page_Load);
            CbEntity.AutoPostBack           =   true;
            CbEntity.SelectedIndexChanged   +=  new Telerik.Web.UI.RadComboBoxSelectedIndexChangedEventHandler(CbEntity_SelectedIndexChanged);
            CbQueries.AutoPostBack          =   true;
            CbQueries.SelectedIndexChanged  +=  new RadComboBoxSelectedIndexChangedEventHandler(CbQueries_SelectedIndexChanged);
            RadGrid1.NeedDataSource         +=  new GridNeedDataSourceEventHandler(RadGrid1_NeedDataSource);
            RadGrid1.ColumnCreated          +=  new GridColumnCreatedEventHandler(RadGrid1_ColumnCreated);
            ADNUpdateModule                 +=  new PortalModuleEventHandler(CrmBulkDelete_ADNUpdateModule);
            CbQueryType.AutoPostBack        =   true;
            CbQueryType.SelectedIndexChanged+=  new RadComboBoxSelectedIndexChangedEventHandler(CbQueryType_SelectedIndexChanged);
            RadGridJobs.NeedDataSource      +=  new GridNeedDataSourceEventHandler(RadGridJobs_NeedDataSource);
            RefreshBtn.Click                +=  new ImageClickEventHandler(RefreshBtn_Click);
            CbStates.AutoPostBack           =   true;
            CbStates.SelectedIndexChanged   +=  new RadComboBoxSelectedIndexChangedEventHandler(CbStates_SelectedIndexChanged);
        }


        #endregion Std overrides
    }
}